I'm trying to strongly type (such as it is) some URLs for a web app when I build a viewmodel.
So I have something like:
new MyModel {
Text = "Foo",
Url = new UrlHelper(Request.RequestContext).Action("MyAction")
}
This works just fine in a controller method, but I have another situation where I am not receiving the Request.Context because it's being called in another class.
Is there another way to do this so that I'm not using "magic strings" and/or relying on the context object?
Use Reference
HttpContext.Current
which is derived from system.web. There for following code will work anywhere in your application.
UrlHelper objUrlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);
objUrlHelper.Action("About");
Example:
public class MyViewModel
{
public int ID { get; private set; }
public string Link
{
get
{
UrlHelper objUrlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);
return objUrlHelper.Action("YourAction", "YourController", new { id = this.ID });
}
}
public MyViewModel(int id)
{
this.ID = id;
}
}
Related
I'm developing an Azure Mobile App service to interface to my Xamarin application.
I've created, connected and successfully populated an SQL Database, but when I try to add some filters to my request, for example an orderby() or where() clauses, it returns me a Bad Request error.
For example, this request: https://myapp.azurewebsites.net/tables/Race?$orderby=iRound%20desc,iYear%20desc&$top=1&ZUMO-API-VERSION=2.0.0 gives me {"message":"The query specified in the URI is not valid. Could not find a property named 'IYear' on type 'MyType'."}.
My configuration method is this:
HttpConfiguration config = new HttpConfiguration();
new MobileAppConfiguration()
.AddTablesWithEntityFramework()
.ApplyTo(config);
config.MapHttpAttributeRoutes();
Database.SetInitializer(new CreateDatabaseIfNotExists<MainDataContext>());
app.UseWebApi(config);
and my DbContext is this:
public class MainDataContext : DbContext
{
private const string connectionStringName = "Name=MS_TableConnectionString";
public MainDataContext() : base(connectionStringName)
{
Database.Log = s => WriteLog(s);
}
public void WriteLog(string msg)
{
System.Diagnostics.Debug.WriteLine(msg);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(
new AttributeToColumnAnnotationConvention<TableColumnAttribute, string>(
"ServiceTableColumn", (property, attributes) => attributes.Single().ColumnType.ToString()));
}
public DbSet<Race> Race { get; set; }
public DbSet ...ecc...
}
Following this guide, I added a migration after creating my TableControllers. So the TableController for the example type shown above is pretty standard:
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]
public class RaceController : TableController<Race>
{
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
MainDataContext context = new MainDataContext();
DomainManager = new EntityDomainManager<Race>(context, Request);
}
// GET tables/Race
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]
public IQueryable<Race> GetAllRace()
{
return Query();
}
// GET tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public SingleResult<Race> GetRace(string id)
{
return Lookup(id);
}
// PATCH tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task<Race> PatchRace(string id, Delta<Race> patch)
{
return UpdateAsync(id, patch);
}
// POST tables/Race
public async Task<IHttpActionResult> PostRace(Race item)
{
Race current = await InsertAsync(item);
return CreatedAtRoute("Tables", new { id = current.Id }, current);
}
// DELETE tables/Race/48D68C86-6EA6-4C25-AA33-223FC9A27959
public Task DeleteRace(string id)
{
return DeleteAsync(id);
}
}
As you can see, I already tried to add the EnableQuery attribute to my TableController, as seen on Google. I also tried to add these filters to the HttpConfiguration object, without any success:
config.Filters.Add(new EnableQueryAttribute
{
PageSize = 10,
AllowedArithmeticOperators = AllowedArithmeticOperators.All,
AllowedFunctions = AllowedFunctions.All,
AllowedLogicalOperators = AllowedLogicalOperators.All,
AllowedQueryOptions = AllowedQueryOptions.All
});
config.AddODataQueryFilter(new EnableQueryAttribute
{
PageSize = 10,
AllowedArithmeticOperators = AllowedArithmeticOperators.All,
AllowedFunctions = AllowedFunctions.All,
AllowedLogicalOperators = AllowedLogicalOperators.All,
AllowedQueryOptions = AllowedQueryOptions.All
});
I don't know what to investigate more, as things seems to be changing too fast for a newbie like me who's first got into Azure.
EDIT
I forgot to say that asking for the complete table, so for example https://myapp.azurewebsites.net/tables/Race?ZUMO-API-VERSION=2.0.0, returns correctly the entire dataset. The problem occurs only when adding some clauses to the request.
EDIT 2
My model is like this:
public class Race : EntityData
{
public int iRaceId { get; set; }
public int iYear { get; set; }
public int iRound { get; set; }
ecc..
}
and the database table that was automatically created is this, including all the properties inherited from EntityData:
Database table schema
Digging into the source code, Azure Mobile Apps sets up camelCase encoding of all requests and responses. It then puts them back after transmission accordign to rules - so iRaceId becomes IRaceId on the server.
The easiest solution to this is to bypass the auto-naming and use a JsonProperty attribute on each property within your server-side DTO and client-side DTO so that they match and will get encoding/decoded according to your rules.
So:
public class Race : EntityData
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("raceId")]
public int iRaceId { get; set; }
[JsonProperty("year")]
public int iYear { get; set; }
[JsonProperty("round")]
public int iRound { get; set; }
etc..
}
I want my repsository to be independent of the data access technology. Currently I am working on a Xamrin.Forms App that uses Azure Mobile App Services for data access. For performance and flexibility reasons I want my repository to look simmilar like the following:
Task<IEnumerable<IDomainObject>> GetDomainObjectAsync(Func<IQueryable<IDomainObject>, IQueryable<IDomainObject>> query)
Suppose my IDomainObject looks like the following:
public interface IDomainObject
{
string Name { get; }
}
and my DataAccess Object:
internal class AzureDomainObject : IDomainObject
{
public string Name { get; set; }
public string Id { get; set; }
}
As far as I found out and tested I can do the following to query the database within my repository implementation:
public async Task<IEnumerable<IDomainObject>> GetDomainObjectAsync(Func<IQueryable<IDomainObject>, IQueryable<IDomainObject>> query)
{
// _table of type IMobileServiceTable<AzureDomainObject> gotten by MobileServiceClient
var tableQuery = _table.GetQuery();
tableQuery.Query = tableQuery.Query.Take(4); // 1) this was for testing and it works (ordering etc also works)
// tableQuery.Query = query(tableQuery.Query); // 2) this was my initial idea how to use the input param
await _table.ReadAsync(tableQuery);
}
My poblem now is how to use the input param query to replace 1) with 2).
tableQuery.Query expects an IQueryable<AzureDomainObject> but query is of type IQueryable<IDomainObject>.
Neither .Cast<AzureDomainObject>() nor .OfType<AzureDomainObject>() work to convert. Nor does (IQueryable<IAzureDomainObject>)query; work.
Cast and OfType throw NotSupportedException and the hard cast throws an InvalidCastException.
I also tried to extract the Expression from the query input param and assign it to the tableQuery.Query. But then a runtime exception occurs that they are not compatible.
Another idea I had was to use the ReadAsync(string) overload and pass the string representation of the passed query param. But this way I don't know how to generate the string.
So the final question is: Does anyone knows how to hide both AzureDomainObject and IMobileServiceTable from the domain model but keep the flexibility and performance benefits of IQueryable in the repository interface?
According to your description, I checked this issue and here is my implementation for this scenario, you could refer to them.
Model:
public class TodoItem : IDomainObject
{
public string Id { get; set; }
[JsonProperty(PropertyName = "text")]
public string Text { get; set; }
[JsonProperty(PropertyName = "complete")]
public bool Complete { get; set; }
}
public interface IDomainObject
{
string Id { get; set; }
}
Repository:
public interface IAzureCloudTableRepository<T> where T : IDomainObject
{
Task<IEnumerable<T>> GetDomainObjectAsync(Func<IQueryable<T>, IQueryable<T>> query);
}
public class AzureCloudTableRepository<T> : IAzureCloudTableRepository<T> where T : IDomainObject
{
IMobileServiceTable<T> table;
public AzureCloudTableRepository(MobileServiceClient client)
{
this.table = client.GetTable<T>();
}
public async Task<T> CreateItemAsync(T item)
{
await table.InsertAsync(item);
return item;
}
public async Task<IEnumerable<T>> GetDomainObjectAsync(Func<IQueryable<T>, IQueryable<T>> query)
{
var tableQuery = this.table.CreateQuery();
tableQuery.Query = tableQuery.Query.Take(4); //the internal fixed query
tableQuery.Query = query(tableQuery.Query); //the external query
return await tableQuery.ToEnumerableAsync();
}
}
TEST:
var mobileService = new MobileServiceClient("https://{your-app-name}.azurewebsites.net");
var todoitem = new AzureCloudTableRepository<TodoItem>(mobileService);
var items = await todoitem.GetDomainObjectAsync((query) =>
{
return query.Where(q => q.Text!=null);
});
What I'm trying to do is create a site in Orchard that doesn't have a way for a user to register. An administrator will create the users.
What I have is module that defines the parts, records, views, etc. That is basically working.
Now what I'm trying to do is add a UserPart (from Orchard.Users) to one of the parts in my module.
I'm not sure how to do that. I need the fields displayed for the UserPart with the fields for the parent part in the same view. This also needs to be done in a way that when a save happens, all of the UserPart fields get sent to the Orchard.Users module.
Any suggestions, pointers or links on how to do that?
Thanks!
UPDATE...
The Activating Filter is an interesting idea. I initially chose the migration route. For now, I'll try and get that method working.
For simplicity, let's say I have a "Company" type (there's more to the actual type) that has a "CompanyName" and a UserPart.
Here's what the different pieces look like...
Migrations.cs (simplified)
public int Create()
{
SchemaBuilder.CreateTable("CompanyPartRecord", table => table.ContentPartRecord()
.Column("CompanyName", DbType.AnsiString, c => c.WithLength(50))
.Column("UserId", DbType.Int32));
SchemaBuilder.CreateForeignKey("FK_CompanyPartRecord_UserPartRecord", "CompanyPartRecord", new[] {"UserId" }, "Orchard.Users", "UserPartRecord", new[] { "Id" })
ContentDefinitionManager.AlterTypeDefinition("Company", type => type.WithPart("CommonPart").WithPart("UserPart"));
}
CompanyPartRecord
public class CompanyPartRecord : ContentPartRecord
{
public virtual string CompanyName { get; set; }
public virtual int? UserId { get; set; }
}
CompanyPart
public class CompanyPart : ContentPart<CompanyPartRecord>
{
internal LazyField<UserPart> UserPartField = new LazyField<UserPart>();
public string CompanyName
{
get { return Record.CompanyName; }
set { Record.CompanyName = value; }
}
public UserPart User
{
get { return UserPartField.Value;}
set { UserPartField.Value = value; }
}
}
Handler
public class CompanyPartHandler : ContentHandler
{
private readonly IContentManager _manager;
public CompanyPartHandler(IRepository<CompanyPartRecord> repository, IContentManager manager)
{
_manager = manager;
Filters.Add(StorageFilter.For(repository));
OnActivated<CompanyPart>(OnActivatedHandler);
}
private void OnActivatedHandler(ActivatedContentContext context, CompanyPart part)
{
if(part.User == null)
{
part.User = _manager.Create<UserPart>("User");
}
else
{
part.User = _manager.Get<UserPart>(part.User.Id);
}
}
}
Driver
public class CompanyPartDriver : ContentPartDriver<CompanyPart>
{
protected override DriverResult Editor(CompanyPart part, dynamic shapeHelper)
{
return ContentShape("Parts_Company_Edit", () => shapeHelper.EditorTemplate(TemplateName: "Parts/Company",
Model: part, Prefix: Prefix));
}
protected override DriverResult Editor(CompanyPart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
Controller
public class AdminCompanyController : Controller, IUpdateModel
{
private readonly IOrchardServices _services;
private readonly INotifier _notifier;
private readonly IContentManager _contentManager;
private readonly ITransactionManager _transactionManager;
private readonly Localizer T = NullLocalizer.Instance;
public AdminCompanyController(IOrchardServices services)
{
_services = services;
_notifier = services.Notifier;
_contentManager = services.ContentManager;
_transactionManager = services.TransactionManager;
}
public ActionResult Create()
{
var company = _contentManager.New<CompanyPart>("Company");
var model = _contentManager.BuildEditor(company);
return View(model);
}
[HttpPost, ActionName("Create")]
public ActionResult CreatePOST()
{
var contentItem = _contentManager.New<CompanyPart>("Company");
var model = _contentManager.UpdateEditor(contentItem, this);
if (!ModelState.IsValid)
{
_transactionManager.Cancel();
return View(model);
}
_contentManager.Create(contentItem.ContentItem);
_notifier.Information(T("Company has been saved"));
return RedirectToAction("Index");
}
public ActionResult Edit(int Id)
{
var contentItem = _services.ContentManager.Get(Id);
dynamic model = _services.ContentManager.BuildEditor(contentItem);
return View(model);
}
[HttpPost, ActionName("Edit")]
public ActionResult EditPOST(int Id)
{
var contentItem = _contentManager.Get<CompanyPart>(Id);
var model = _contentManager.UpdateEditor(contentItem, this);
_notifier.Information(T("Company has been saved"));
return RedirectToAction("Index");
}
public ActionResult Delete(int Id)
{
var contentItem = _contentManager.Get<CompanyPart>(Id);
_contentManager.Destroy(contentItem.ContentItem);
return RedirectToAction("Index");
}
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties)
{
return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
}
public void AddModelError(string key, LocalizedString errorMessage)
{
ModelState.AddModelError(key, errorMessage.ToString());
}
}
View (create)
#{ Layout.Title = T("Add Company").ToString(); }
#using (Html.BeginFormAntiForgeryPost())
{
#Display(Model)
}
Editor Template
#model SDS.Models.CompanyPart
<fieldset>
#Html.LabelFor(m => m.CompanyName)
#Html.TextBoxFor(m => m.CompanyName)
</fieldset>
#*
What goes here to display UserPart?
*#
So here's where I'm at. I can see the ContentItem (CompanyType). I can put in the name and save it. The name is getting saved to the db. Right now the UserPart is getting saved to the db, but all of the fields are blank.
The part I'm stuck on is what to put in the editor template to display the UserPart fields so that the values get to the UserPart driver and ultimately the db.
Any ideas on how to do that?
Thanks!
So you don't attach parts to parts, you attach parts to content items, and you can do that in multiple ways.
You can do it through the admin screen, but that isn't a code driven solution and would have problems if you have multiple environments or need to redeploy a fresh version of code.
You can attach the part when you create a new content item in the migration. This might be a good solution, if you already ran your migration you could possibly do it with an update migration. This allows the part to be managed through the admin screen, but has downsides because it can be removed and if you have code that relies on the part then you will start having errors.
The last way and best way is to attach the part dynamically using an Activating Filter.
ActivatingFilter class - Attaches a part to a content type from code. As opposed to attaching parts via migrations, parts attached using this filter will neither be displayed in the Dashboard, nor users will be able to remove them from types. It's a legitimate way of attaching parts that should always exist on a given content type.
So to do this:
1. Add a reference to Orchard.Users to your custom project.
2. Create a handler for you part. Such as MyPartHandler
3. Then add the activating handler like so
Filters.Add(ActivatingFilter.For<UserPart>("MyContentType"));
So now anywhere in your code you can access the UserPart if you already have your part, or the content item using
var userPart = myPart.As<UserPart>();
I am trying to learn IOC principle from this screencast
Inversion of Control from First Principles - Top Gear Style
I tried do as per screencast but i get an error while AutomaticFactory try create an object of AutoCue. AutoCue class has contructor which takes IClock and not SystemClock. But my question is , in screencast IClock is resolved with SystemClock while inside AutomaticFactory .But in my code , IClock does not get resolved . Am i missing something ?
class Program
{
static void Main(string[] args)
{
//var clarkson = new Clarkson(new AutoCue(new SystemClock()), new Megaphone());
//var clarkson = ClarksonFactory.SpawnOne();
var clarkson = (Clarkson)AutomaticFactory.GetOne(typeof(Clarkson));
clarkson.SaySomething();
Console.Read();
}
}
public class AutomaticFactory
{
public static object GetOne(Type type)
{
var constructor = type.GetConstructors().Single();
var parameters = constructor.GetParameters();
if (!parameters.Any()) return Activator.CreateInstance(type);
var args = new List<object>();
foreach(var parameter in parameters)
{
var arg = GetOne(parameter.ParameterType);
args.Add(arg);
}
var result = Activator.CreateInstance(type, args.ToArray());
return result;
}
}
public class Clarkson
{
private readonly AutoCue _autocue;
private readonly Megaphone _megaphone;
public Clarkson(AutoCue autocue,Megaphone megaphone)
{
_autocue = autocue;
_megaphone =megaphone;
}
public void SaySomething()
{
var message = _autocue.GetCue();
_megaphone.Shout(message);
}
}
public class Megaphone
{
public void Shout(string message)
{
Console.WriteLine(message);
}
}
public interface IClock
{
DateTime Now { get; }
}
public class SystemClock : IClock
{
public DateTime Now { get { return DateTime.Now; } }
}
public class AutoCue
{
private readonly IClock _clock;
public AutoCue(IClock clock)
{
_clock = clock;
}
public string GetCue()
{
DateTime now = _clock.Now;
if (now.DayOfWeek == DayOfWeek.Sunday)
{
return "Its a sunday!";
}
else
{
return "I have to work!";
}
}
}
What you basically implemented is a small IoC container that is able to auto-wire object graphs. But your implementation is only able to create object graphs of concrete objects. This makes your code violate the Dependency Inversion Principle.
What's missing from the implementation is some sort of Register method that tells your AutomaticFactory that when confronted with an abstraction, it should resolve the registered implementation. That could look as follows:
private static readonly Dictionary<Type, Type> registrations =
new Dictionary<Type, Type>();
public static void Register<TService, TImplementation>()
where TImplementation : class, TService
where TService : class
{
registrations.Add(typeof(TService), typeof(TImplementation));
}
No you will have to do an adjustment to the GetOne method as well. You can add the following code at the start of the GetOne method:
if (registrations.ContainsKey(type))
{
type = registrations[type];
}
That will ensure that if the supplied type is registered in the AutomaticFactory as TService, the mapped TImplementation will be used and the factory will continue using this implementation as the type to build up.
This does mean however that you now have to explicitly register the mapping between IClock and SystemClock (which is a quite natural thing to do if you're working with an IoC container). You must make this mapping before the first instance is resolved from the AutomaticFactory. So you should add the following line to to the beginning of the Main method:
AutomaticFactory.Register<IClock, SystemClock>();
I have small WPF application. There are 5 projects in solution.
I want separate DOMAIN classes with UI ENTITIES and I want to use AUTOMAPPER.
You can download whole solution here: TestWPFAutomapper.zip
Domain class(Domain.Source.cs) with UI Entity(Entities.Destination.cs) have same signature.
In Entities.Destination.cs I would like to put other logic.
namespace DOMAIN
{
public class Source
{
public int Id { get; set; }
public int Position { get; set; }
}
}
using System.ComponentModel;
namespace ENITITIES
{
public class Destination : INotifyPropertyChanged
{
private int _id;
private int _position;
public int Id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("Id");
}
}
public int Position
{
get { return _position; }
set
{
_position = value;
OnPropertyChanged("Position");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
My data comes from DAL.DataContext using Entity Framework with CodeFirst. Here I´m using Source class.
using System.Data.Entity;
using DOMAIN;
namespace DAL
{
public class DataContext : DbContext
{
public DbSet<Source> Sources { get; set; }
}
}
Mapping is in BL.MyAppLogic.cs . In this class I have property Items which is ObservableCollection.
After puting another item into DB for Source class collection get refresh but for Destination is not refreshing.
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Linq;
using AutoMapper;
using DAL;
using DOMAIN;
using ENITITIES;
namespace BL
{
public class MyAppLogic
{
private readonly DataContext _dataContext = new DataContext();
public ObservableCollection<Source> Items { get; set; }
//public ObservableCollection<Destination> Items { get; set; }
public MyAppLogic()
{
Database.SetInitializer(new MyInitializer());
Mapping();
_dataContext.Sources.Load();
Items = _dataContext.Sources.Local;
//Items = Mapper.Map<ObservableCollection<Source>, ObservableCollection<Destination>>(_dataContext.Sources.Local);
}
private void Mapping()
{
Mapper.CreateMap<Source, Destination>().ReverseMap();
// I tried also Mapper.CreateMap<ObservableCollection<Source>, ObservableCollection<Destination>>().ReverseMap();
}
public int GetLastItem()
{
return _dataContext.Database.SqlQuery<int>("select Position from Sources").ToList().LastOrDefault();
}
public void AddNewItem(Destination newItem)
{
_dataContext.Sources.Add(Mapper.Map<Destination, Source>(newItem));
_dataContext.SaveChanges();
}
}
}
My problem is not with mapping, that’s works good, but with refreshing collection after adding or removing items from db. If I use DOMAIN.Source class everything works, collection is refreshing. But when I’m using ENTITIES.Destination data comes from DB and also I can put som new data to DB but refresing ObservableCollection is not working.
Please try to comment lines(14 & 23) in BL.MyAppLogic.cs and uncomment(15 & 24) and you’ll see what I mean.
Thank you for any help.
I got it but I don´t know if is correct.
Local has CollectionChanged event
so in constructor I put these lines
public MyAppLogic()
{
Database.SetInitializer(new MyInitializer());
Mapping();
_dataContext.Sources.Load();
_dataContext.Sources.Local.CollectionChanged += SourcesCollectionChanged;
Items = Mapper.Map<ObservableCollection<Source>, ObservableCollection<Destination>>(_dataContext.Sources.Local);
}
and handler looks
private void SourcesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
var source = sender as ObservableCollection<Source>;
Mapper.Map(source, Items);
}
Now is my collection automating refreshing when I put something to DB in my UI.
Looks like automapper don´t put reference into Items, but create new instance.