Can't seem to expand an Image Navigation property with Breeze JS - entity-framework-5

I am using MVC, Entity Framework, Durandal and Breeze JS. I've got a user which looks like such (simplified):
public class User : EntityBase<Guid>, IAggregateRoot
{
public Guid Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[ForeignKey("UserImage")]
public virtual Guid? ImageId { get; set; }
public virtual UserImage UserImage { get; set; }
}
The UserImage class looks like such. I know I should limit the size of the Image. (Maybe this is the issue?):
public class UserImage
{
public Guid Id { get; set; }
[MaxLength]
public byte[] Image { get; set; }
}
I've got an api function on the server to get the current user:
public IQueryable<User> GetCurrentUser()
{
IPrincipal principal = HttpContext.Current.User;
var users = _uow.Users.FindBy(u => u.UserName.Equals(principal.Identity.Name));
if (!users.Any())
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized));
}
return users;
}
And two calls on the client which get the current user. The first is in the shell:
function loadCurrentUser() {
return uow.CurrentUser.all().then(function (newUser) {
log('Welcome to the Site ' + newUser[0].FullName() + '!', newUser[0], true);
config.CurrentUser(newUser[0]);
return true;
});
}
The second is in a ManageUser viewmodel:
function activate() {
return uow.CurrentUser.all(['UserImage']).then(function (user) {
self.CurrentUser(user[0]);
return $.when(init()).then(boot());
}).fail(function() {
return router.activate('accounts/login');
});
}
Now I can load an Image into the ManageUser page and save and in fiddler it shows that the ImageId and Image are being sent across to the server. Then I checked the BeforeSaveEntity intercept and shows two entities being saved.
Updated User with ImageId set
New UserImage
The data is also visible in the database. Now when I refresh the Manage User page I can see the two GetCurrentUser calls in fiddler.
From the shell call I can see that the User is being returned and an ImageId is set but no UserImage was sent over because didn't expand the query.
From the Manage User call I see the User is returned but only the ImageId is sent over and the Image object was OMITTED from the JSON.
Has anyone come across this issue with images? All my other expands appear to be working correctly. Does anyone have any examples on using breeze to save just the filepath to the image and possibly using windows azure for media storage?

I know this probably won't answer your question but I would propose not sending the byte array to the client and rather have an Image Handler on the server side that takes an ImageId as a parameter and then return the image with the relevant Content Type set. An example of this can be found here.
By using this approach you could reference your image from HTML using an img tag with the source set to the Image Hander with the relevant ImageId.
An example using knockout for data binding would be:
<a data-bind="attr: {href: '/Image/' + User.ImageId()}"></a>
This approach enables you to easily add caching on both the server and client which will improve performance. It also removed the need to convert the byte array to an image on the client side, which may or may not be a pain.
Edit:
When saving the managed user, post the Image to an Upload action on the ImageHandler (have a look at this article). This action must return the new Id of the image. After you've received the new Id, update the User.ImageId on client side and call SaveChanges on breeze.

Related

display selected file after view reload in ASP.NET MVC 5

I am working on a project in ASP.NET MVC 5, there is a requirement to upload image file, I can upload the image to the server, what I am facing is when there is an error in the view submitted I return the view model with some error message to be displayed on the view so that the user is not requested to select the image again, all the form fields data in the view are getting populated except the file which previously selected is not. Please help me the way to achieve this.
Here is the code I have to select the image
#Html.TextBoxFor(model => Model.Logo, null, new { type = "file"})
here is the model field
[DataType(DataType.Upload)]
public HttpPostedFileBase Logo
{
get;
set;
}
For security reasons, you cannot set the value of a file input in the controller, so if you want to avoid having the user select the file again if you return the view, you must first save the file (it could be to a temporary location) and display an indicator to the user that the file has been uploaded. For example, your model might contain the following properties
public HttpPostedFileBase Logo { get; set; }
public string LogoFileName { get; set; }
public string LogoPath { get; set; }
and in the controller method, save the file and set the FileName (i.e. model.LogoFileName = Logo.FileName;) and Path properties. Then in the view, add a conditional statement to render the FileName if it exists, or display the file input if it does not, for example
if(Model.LogoFileName == null)
{
#Html.TextBoxFor(m => m.Logo, new { type = "file" })
}
else
{
#DisplayFor(m => m.LogoFileName)
}

How to add Identity 2.0 users to an object

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.

Intermittent Validation Problems using ServiceSttack

I'm using ServiceStack to build an API and at the same time, I'm using the plugin that allows Razor views to return html to browsers.
I have validation set up and configured correctly. I know this because I get the validation messages on the corresponding Razor view and the messages are accurate. However, if I modify the Razor view at all (and by "at all" I mean something as simple as adding a line break and then immediately deleting it), I get a 500 error accompanied by a blank page.
Other times, while in the process of simply refreshing the page to review styling of the Razor view, the validation simply returns a blank page with the same non-useful 500 error. And of course, if I remove the validation, the Razor view renders just fine 100% of the time.
What must I do to have validation working at all times? My code is straight forward and matches everything that I've been able to read in the Docs. Namely, both the response and the requests are in the same namespace and the validator is coded to the request.
Here is the request DTO
namespace MyServer.DTO
{
[Validator(typeof(SignUpValidator))]
[Route("SignUp")]
public class SignUp : IReturn<SignUpResponse>
{
public string UserName { get; set; }
public string Email { get; set; }
public string EmailConfirm { get; set; }
public string Password { get; set; }
public string PasswordConfirm { get; set; }
public int UserId { get; set; }
}
}
Here is the corresponding validator
namespace MyServer.DTO
{
public class SignUpValidator : AbstractValidator<SignUp>
{
public SignUpValidator()
{
RuleSet(ApplyTo.Post, () =>
{
RuleFor(e => e.UserName).NotEmpty();
RuleFor(e => e.Email).NotEmpty();
RuleFor(e => e.EmailConfirm).NotEmpty();
RuleFor(e => e.Password).NotEmpty();
RuleFor(e => e.PasswordConfirm).NotEmpty();
}
);
}
}
}
Here is the response
namespace MyServer.DTO
{
public class SignUpResponse
{
bool DidSucceed { get; set; }
int NewUserId { get; set; }
public ResponseStatus ResponseStatus { get; set; }
}
}
And lastly, here is the code that configures the validation plugin
Plugins.Add(new ValidationFeature());
Container.RegisterValidators(typeof(SignUpService).Assembly);
As you can see, everything is pretty vanilla and by the book, but this setup is very fragile for some reason. Any modification to the corresponding Razor view and I get the aforementioned errors. Then I have to recompile repeatedly until it works again.
I should also mention that, if I use the REST Console (google chrome extension thingy) to test this, I get the following results when posting to the exact same URI:
Content-Type set to : application/json - Everything works as intended. 400 Response with the errors listed in the response body.
Content-Type set to : application/html - Consistently breaks. 500 Response with no data in the response body.
There must be something that I'm missing.
Thanks so much for your time and I would appreciate any help.
Thanks again.

ContentManager.Create does nothing

I am trying to build a service in Orchard that allows me to create content through a custom form on a page. The service and the content type definitions look fine to me, but somehow, eventhough I don't get any errors or other signs in the Orchard log files, creating new content using the IContentManager does nothing for me.
Parts involved
The controller accepting the form values
[HttpPost]
public ActionResult Create(CreateSopViewModel viewModel)
{
if(!ModelState.IsValid)
{
var shape = _shape.CreateContent();
shape.Header = _shape.Parts_Title(Title: "New item");
// Add the original fields to the shape.
shape.Title = viewModel.Title;
shape.Description = viewModel.Description;
shape.InitialComments = viewModel.InitialComments;
return new ShapeResult(this, shape);
}
// Store the new procedure in the database
_service.CreateContentItem(
viewModel.Title,viewModel.Description,viewModel.InitialComments);
// Redirect the user back to the homepage.
return Redirect("~/");
}
The service that contains the CreateContentItem method:
public void CreateContentItem(string title, string description, string initialComments)
{
// Initialize a new content item based on the SOP type
var customPart = _services.ContentManager.New<MyCustomPart>("CustomContentType");
customPart.Description = description;
customPart.Identifier = BuildIdentifier(title);
customPart.ContentItem.As<TitlePart>().Title = title;
_services.ContentManager.Create(customPart.ContentItem);
}
The content part + record
public class MyCustomPart: ContentPart<MyCustomPartRecord>
{
[Required]
public string Identifier
{
get { return Record.Identifier; }
set { Record.Identifier = value; }
}
[Required]
public string Description
{
get { return Record.Description; }
set { Record.Description = value; }
}
}
public class MyCustomPartRecord: ContentPartRecord
{
public virtual string Identifier { get; set; }
public virtual string Description { get; set; }
}
The migration
SchemaBuilder.CreateTable(typeof(MyCustomPartRecord).Name, table => table
.ContentPartRecord()
.Column<string>("Description")
.Column<string>("Identifier"));
ContentDefinitionManager.AlterPartDefinition("StandardOperationalProcedurePart", builder => builder
.Attachable(true));
ContentDefinitionManager.AlterTypeDefinition("CustomContentType", builder => builder
.DisplayedAs("Custom Content Type")
.WithPart("TitlePart")
.WithPart("MyCustomPart")
.Creatable(true));
Question
Again, I don't get any errors, not in the log and not in Visual Studio. However, my new content item doesn't get created or at least, I can't see it in the admin section of the site under Content.
What is going on and how can I debug this behavior?
I had a similar problem, which was solved when I used the overloaded Create method taking a VersionOptions enum value:
content.Create(customPart.ContentItem, VersionOptions.Published);
This should work even if the content item is not creatable, as mine isn't.
I had a similar issue. In my case the item did appear eventually, but not right away.
The solution for me was to do:
_contentManager.Flush();
I was having this issue, in my case it was that I actually had an error in the database (trying to put 100+ characters into a field that would only hold 100!).
I found the error I was getting (null id in Orchard.Indexing.Models.IndexingTaskRecord entry (don't flush the Session after an exception occurs) ), actually masked the issue. I had to go hunt in the logs to find the real problem.
So anyway, my advice is if you see that contentmanager.create seems to be doing nothing, and any errors don't seem to help, check the logs carefully. They can be found in the logs sub-folder of the appdata folder in the main Orchard.Web project. Because as I've found in the last 48 hours, often the answer is there.

Orchard CMS, Merging templates

I am developing a Widget to show Content pushes on the home page. The push model is as below.
public class PushRecord : ContentPartRecord
{
public virtual string Header { get; set; }
public virtual string Text { get; set; }
public virtual string Url { get; set; }
}
On the admin, I modified the ContentType of the Push Widget to add Media Picker Field. I would like to make a hyper link around the image with Url provided by PushPart. Npw the widget is rendered by two templates, Parts.Push.cshtml and Fields.MediaPicker-PushWidget-Image.cshtml. How do I merge these two and make my Push rendering possible? Any help is greatly appreciated.
Maybe try suppressing display of mediapickerfield via placement.info, and then explicitly render the image with the hyperlink from the .cshtml of the PushPart. You can access the MediaPickerField url like this:
#{
var pushPart = Model.ContentPart;
var photoUrl = pushPart.MediaPickerFieldName.Url;
}
<img src="#photoUrl" ... />

Resources