Howdy, I have what should be a simple question. I have a set of validations that use System.CompontentModel.DataAnnotations . I have some validations that are specific to certain view models, so I'm comfortable with having the validation code in the same file as my models (as in the default AccountModels.cs file that ships with MVC2). But I have some common validations that apply to several models as well (valid email address format for example). When I cut/paste that validation to the second model that needs it, of course I get a duplicate definition error because they're in the same namespace (projectName.Models). So I thought of removing the common validations to a separate class within the namespace, expecting that all of my view models would be able to access the validations from there. Unexpectedly, the validations are no longer accessible. I've verified that they are still in the same namespace, and they are all public. I wouldn't expect that I would have to have any specific reference to them (tried adding using statement for the same namespace, but that didn't resolve it, and via the add references dialog, a project can't reference itself (makes sense).
So any idea why public validations that have simply been moved to another file in the same namespace aren't visible to my models?
CommonValidations.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
namespace ProjectName.Models
{
public class CommonValidations
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public sealed class EmailFormatValidAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if (value != null)
{
var expression = #"^[a-zA-Z][\w\.-]*[a-zA-Z0-9]#[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$";
return Regex.IsMatch(value.ToString(), expression);
}
else
{
return false;
}
}
}
}
}
And here's the code that I want to use the validation from:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Growums.Models;
namespace ProjectName.Models
{
public class PrivacyModel
{
[Required(ErrorMessage="Required")]
[EmailFormatValid(ErrorMessage="Invalid Email")]
public string Email { get; set; }
}
}
You have declared EmailFormatValidAttribute as a subclass to CommonValidations. As such you need to reference it like CommonValidations.EmailFormatValidAttribute. Or alternatively move the EmailFormatValidAttribute class out of the CommonValidations class.
This should work:
[CommonValidations.EmailFormatValid(ErrorMessage="Invalid Email")]
public string Email { get; set; }
By the way, you can simplify your class as follows:
public class EmailFormatValidAttribute : RegularExpressionAttribute
{
public EmailFormatValidAttribute() :
base(#"^[a-zA-Z][\w\.-]*[a-zA-Z0-9]#[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$")
{}
}
Also, take a look at this: Data Annotations Extensions. It's a great DataAnnotations library which has already the most common validations included in it.
Related
I have a class which I want to get the Cloudscribe SiteId but I am unsure how to access it.
If I was using a controller then I would be able to rely on the SiteContext to be DI'd into the constructor but that does not work in this instance.
Can someone tell me how I can access the SiteId property in the code below?
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace CoLabR
{
public class Messaging : Hub
{
private string _siteId;
public Messaging()
{
//Code here to get SiteID
_siteId = "<<Code for getting Site ID";
}
public Task Post(string message)
{
return Clients.Group(_siteId).InvokeAsync("Post", message);
}
public Task JoinRoom()
{
Groups.AddAsync(Context.ConnectionId, _siteId);
return Clients.Group(_siteId).InvokeAsync("Post", Context.User.Identity.Name + " joined. " + _siteId);
}
}
}
SignalR Core supports injecting into hubs so you should be able to just inject SiteContext into Messaging.
Here is a related question that shows how to do that.
Although the answer from #Pawel helped me, my final solution differed. I tried registering SiteContext in my Startup.cs and injecting SiteContext into my constructor for the Hub but it kept returning a different SiteID on each page load (or if I used Singleton it gave me the same SiteId across multiple CloudScribe Sites).
In the end my solution was to change the Hub constructor to the below code
public Messaging(SiteUserManager<SiteUser> userManager)
{
//Code here to get SiteID
_siteId = userManager.Site.Id.ToString();
}
This gave me a consistent SiteId for each Tenant site in my application.
I have an error in the following code when using the Headers property:
Public Function UploadImage(image As String) As String
Dim wc As System.WebClient
'create WebClient
Set wc = CreateObject("System.Net.WebClient")
Call wc.Headers.Add("Authorization", "Client-ID " & ClientId) <------- Error occurs here
I have repro'd your issue as far as possible. You didn't mention what the error you were getting was, but I got:
Automation error -2146233079 (80131509)
I tried using
CallByName(wc, "Headers", VbGet)
... but that just returns
Automation error 440.
Oh well...
I looked up online, and found this link. My guess is that because the base class of the WebHeaderCollection class is not COM Visible, this causes the error.
My work-around would be to wrap up this functionality in a small .NET component, and make that COM visible.
An example of this would be something like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
namespace WebClientWrapper
{
[ComVisible(true)]
[Guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]
public class WebClientWrapper : WebClient
{
[ComVisible(true)]
public WebHeaderCollectionWrapper WHeaders
{
get
{
return new WebHeaderCollectionWrapper(base.Headers);
}
}
}
[ComVisible(true)]
[Guid("yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy")]
public class WebHeaderCollectionWrapper
{
WebHeaderCollection _whc;
internal WebHeaderCollectionWrapper(WebHeaderCollection whc)
{
_whc = whc;
}
[ComVisible(true)]
public void Add(string name, string value)
{
_whc.Add(name, value);
}
[ComVisible(true)]
public void Clear()
{
_whc.Clear();
}
}
}
(You will have to replace the GUIDs with your own values - use GUIDGEN.EXE).
Instantiate this component with CreateObject("WebClientWrapper.WebClientWrapper")
Now you would simply replace references to the Headers property to WHeaders (or whatever you want to call it). WHeaders gives you a true wrapper around WebHeaderCollection - you'll have to define all the other wrapped methods and properties yourself. I was hoping to define WHeaders as public WebHeaderCollectionWrapper, but that didn't seem to work.
Since WebClientWrapper inherits from WebClient, you should be able to use most of the properties and methods. Where you have troubles, just add new methods to the class wrapping up the functionality that doesn't work with VB.
Oh, and do remember to set the checkbox at Project Properties => Build => Output => Register for COM interop. And then reference the type library created.
I have one project with EF and Code First approach and there using of Data Annotations was straight forward. Now I'm working with Database First and I see that using Data Annotations is more specific so I want to know the right steps to implement it.
The structure of my project that provides Data Access is this:
In ModelExtensions are all my files that I've created to add the Data Annotations to the DbContextModel.tt entities.
Here is the structure of one of my files in ModelExtensions:
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
namespace DataAccess.ModelExtensions
{
[MetadataType(typeof(MCS_ContentTypesMetaData))]
public partial class MCS_ContentTypes : BaseEntity
{
}
internal sealed class MCS_ContentTypesMetaData
{
[Required]
[StringLength(10)]
public string Name { get; set; }
}
}
I have several questions here. First - the namespace. Should it be like this namespace DataAccess.ModelExtensions or I have to remove the .ModelExtensions part. I was looking at a project using DB first and there the namespace was just DataAccess not sure why it is needed (if so). Also - Do I need to add some other references to the DbContextModel.tt entities? Now I use standard C# classes for this and then rename them to : public partial class MCS_ContentTypes : BaseEntity. Do I have to use a special approach for creating those to explicitly expose the connection between the entity and this file?
1) The namespace of your extension models must be the same as the namespace of EF auto-generated entity classes - If the namespace of DbContextModel.tt entity classes is DataAccess, you should set the namespace of your classes to DataAccess.
2) I doesn't get your question completely, however in this approach, names of entity classes and your classes must be the same.
The following example shows what it should be. Suppose that EF generates the following entity class for you:
namespace YourSolution
{
using System;
using System.Collections.Generic;
public partial class News
{
public int ID { get; set; }
public string Title { get; set; }
}
}
So, your partial classes should be like the following:
namespace YourSolution
{
[MetadataType(typeof(NewsAttribs))]
public partial class News
{
// leave it empty.
}
public class NewsAttribs
{
// Your attribs will come here.
[Display(Name = "News title")]
[Required(ErrorMessage = "Please enter the news title.")]
public string Title { get; set; }
// and other properties you want...
}
}
So, you doesn't need any : BaseEntity inheritance.
I am trying to implement the following code to map a database to a POCO that has slightly different Property names then the corresponding columns, and a slightly different class name than the table name.
Here's the article
The problem is there is no method called MapSingleType. I have the following two methods available:
public EntityTypeConfiguration<TEntityType> Map<TDerived>(Action<EntityMappingConfiguration<TDerived>> derivedTypeMapConfigurationAction) where TDerived : class, TEntityType;
public EntityTypeConfiguration<TEntityType> Map(Action<EntityMappingConfiguration<TEntityType>> entityMappingConfigurationAction);
However, I can't find any examples of how to use them and I'm hoping to find the MapSingleType method since that works perfect for the very large number of columns I have to map.
I have downloaded CTP4 from here
And I am using EF 4.0.3
Here is the code I have so far:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
using PlayingWithEF.DAL.CLS.Classes;
using System.Data.Entity.ModelConfiguration;
namespace PlayingWithEF.DAL.CLS.Context
{
public class CLSContext : DbContext
{
public DbSet<Listing> Listings { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Listing>().Map(...?)
}
}
}
You're going to have to map each property individually:
modelBuilder.Entity<Listing>()
.Property(l => l.YourProperty)
.HasColumnName("PropertyName");
modelBuilder.Entity<Listing>()
.ToTable("TableName");
You can't use CTP4 and EF 4.0.3 together, they're different versions of the same library.
I believe this functionality was removed from the betas at some point, and is no longer in the shipping version.
I am trying to create a ViewModel class. After I created class in "ViewModels" folder. My List type declaration are not recognized. My questions and code is below:
Are there some special way of creating ViewModel Classes?
Are ViewModels a methodology, rather than a feature in MVC3?
Can someone please tell me what I have missed thanks -P
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcApplication8.ViewModels
{
//compiler does not recongize List class or SelectListitem
private List<SelectListItem> _products = new List<SelectListItem>();
//compiler does not recongize List class
public List<SelectListItem> products
{
get { return _products; }
}
}
Are there some special way of creating ViewModel Classes?
No, create them like creating any other class. The convention is to place them in the Models folder.
Are ViewModels a methodology, rather than a feature in MVC3?
Kind of. They're not a feature of the framework itself, but a recommendation to keep your View's simple and clean, and simplify model binding.
Can someone please tell me what I have missed thanks
Where's your class declaration?
namespace MvcApplication8.ViewModels
{
public class ThisIsTheClassNameAndMustGoFirst
create a new empty MVC 3 project, using Razor.
add a class definition under Models folder, i.e:
namespace MvcApplication1.Models
{
public class WhateverNameYouWantModel
{
public string Foo { get; set; }
public string Bar { get; set; }
}
}
right click on Controllers folder and add a new controller. the name must end with "Controller". don't bother checking option to add action methods. the controller would look like this:
using System.Web.Mvc;
using MvcApplication1.Models;
namespace MvcApplication1.Controllers
{
public class HelloController : Controller
{
public ActionResult Index()
{
return View(new WhateverNameYouWantModel());
}
}
}
right click on the Index() signature above and choose "Add View". Make sure nothing is checked, the view name matches the action name "Index" and Razor is the engine. add the model type at the top:
#model MvcApplication1.Models.WhateverNameYouWantModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>Index</title>
</head>
<body>
<div>hello world!</div>
</body>
</html>
set the mvc project as your startup project, press F5, the browser will open to http://localhost:xxxx now you will need to point to http://localhost:xxxx/Hello/Index
In asp.net mvc names are everything between the views, actions and controllers. Its all convention, you don't need to stick to it but if you don't you are gonna have to do some extra plumbing.