How to Define Properties in Partial Classes, Then Mark Them with Attributes in Another Partial Class

Can I define properties in partial classes, then mark them with attributes in another partial class?

I've seen something like this done in an article by Scott Guthrie (near the end of it) - didn't try it myself, though.

http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx

[MetadataType(typeof(Person_Validation))]
public partial class Person
{
// Partial class compiled with code produced by VS designer
}

[Bind(Exclude="ID")]
public class Person_Validation
{
[Required(ErrorMessage = "First Name Required")]
[StringLength(50, ErrorMessage = "Must be under 50 characters")]
public string FirstName { get; set; }

[Required(ErrorMessage = "Last Name Required")]
[StringLength(50, ErrorMessage = "Must be under 50 characters")]
public string LastName { get; set; }

[Required(ErrorMessage = "Age Required")]
[Range(0, 120, ErrorMessage = "Age must be between 0 and 120")]
public int Age { get; set; }

[Required(ErrorMessage = "Email Required")]
[Email(ErrorMessage = "Not a valid email")]
public string Email { get; set; }
}

Is it possible to add an attribute to a property in a partial class?

No, you can't.

You can only attach attributes to members you declare there and then, and unless the member is also declared as partial (so that you may reimplement it elsewhere) you cannot attach attributes to members declared in another partial file.

How can I use attributes on a property defined in the other half of a partial class?

You could use MetadataType attribute like this:

[MetadataType(typeof(MyClass_Validation))]     
public partial class MyClass
{}

public class MyClass_Validation
{
[DisplayFormat(...)]
public DateTime StartDate { get; set; }
}

Adding annotations/metadata with partial classes

You cannot define a variable twice. You need to define it in another class and define the new class as part of the class you want with metadatatype.

main class

 public partial class Invoice_Details
{
public int Quantity { get; set; }
}

metadata class

namespace classnamespace
{
public class InvoiceDetailsMetaData
{
[Display(Name = "Quantity")]
public int Quantity { get; set; }
}
[MetadataType(typeof(classnamespace.InvoiceDetailsMetaData))]
public partial class Invoice_Details
{

}
}

Is it possible to have two partial classes in different assemblies represent the same class?

No, you cannot have two partial classes referring to the same class in two different assemblies (projects). Once the assembly is compiled, the meta-data is baked in, and your classes are no longer partial. Partial classes allows you to split the definition of the same class into two files.

Partial Class with Virtual Properties?

Maybe you can modify autogenerated classes to put gets and sets in other file.
Example:

// file: MyClass_1.cs
public partial class MyClass
{
public int MyProperty
{
get { this.MyPropertyGet(); }
set { this.MyPropertySet(value); }
}
}

And in other file:

// file: MyClass_2.cs
public partial class MyClass
{
private int _myProperty;

private int MyPropertyGet()
{
return _myProperty;
}

private void MyPropertySet(int value)
{
_myProperty = value;
}
}

Access class fields from partial class

There are a few things you need to fix with the code you posted:

When using partial classes in C# all parts of the class must be declared as partial classes

You have:

 public class myClass {}
public partial class myClass {}

Which needs to become:

public partial class myClass {}
public partial class myClass {}

Secondly, you're trying to set:

myString="newString";

but myString is a public property without a setter.

So either you add a setter when declaring myString:

public string myString
{
get{ return _myString; }
set { _myString = value; }
}

or just use:

_myString="newString";

in your second partial class file.

Can i call a function defined in one partial class from another partial class. Is it possible?

I think the problem you're experiencing is because your website is dynamically compiled, rather than a precompiled web application.

When the ASP.NET page is first accessed, the compiler will only look in the code behind file, and won't search all the other .cs files for possible extra code for the same class. Thus, it will never see the implementation of display(), which in turn causes the code behind file to fail to compile.

I would certainly not use a method like this for what you seem to want to do, but you could define a custom class that derives from System.Web.UI.Page and have your actual page derive from the new class. The class definition would have to be in App_Code for a dynamically compiled website.

Registration.aspx.cs:

public partial class Web_Pages_Authentication_Login_Management_Registration : MyPage
{
protected void btnSubmit_Click(object sender, EventArgs e)
{
Display();
}
}

App_Code\MyPage.cs:

public class MyPage : System.Web.UI.Page
{
protected void Display()
{
// ...
}
}

In any case: you certainly shouldn't use static variables the way you are using them, this will cause problems if multiple people are using your site at the same time. Frankly, your example doesn't make much sense to me. Maybe if we had more information about what you're trying to accomplish, we could provide a best-practise way to do it.

Note that in your example, putting the second file in App_Code directory will not make it work. That would simply result in two classes with the exact same class name (provided the namespaces match of course). It really doesn't like that :)

Set Attribute Value at Compile Time

Ok, here's a convoluted workaround.

Change your class to a partial class. Put all your logic in one main file. In a separate file, decorate your class with the attributes you want. See Can I define properties in partial classes, then mark them with attributes in another partial class?

Except, make the second class a T4 template.

Something like the following:

Foobar.cs

public partial class Foobar {
// regular code
...
}

FoobarAttributes.tt (syntax highlighting is wrong here)

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#
int major = 1;
int minor = 0;
int revision = 1;
int build = 1;

try
{
// Code here is copied from a template in the Properties
// folder that auto-increments the build version, so that's the file location
string currentDirectory = Path.GetDirectoryName(Host.TemplateFile);
string assemblyInfo = File.ReadAllText(Path.Combine(currentDirectory,"AssemblyInfo.cs"));
Regex pattern = new Regex("AssemblyVersion\\(\"\\d+\\.\\d+\\.(?<revision>\\d+)\\.(?<build>\\d+)\"\\)");
MatchCollection matches = pattern.Matches(assemblyInfo);
revision = Convert.ToInt32(matches[0].Groups["revision"].Value);
build = Convert.ToInt32(matches[0].Groups["build"].Value) + (incBuild?1:0);
}
catch(Exception)
{ }
#>
[AddIn("Foobar", Version = "<#= this.major #>.<#= this.minor #>.<#= this.revision #>.<#= this.build #>")]
public partial class Foobar
{
// ...
}

Set up an event to compile the template on build. Now you can write C# code into the T4 to extract/modify text from other files to give your attribute the correct version info (see above for some example code).

I can't find the SO answer I pulled this from (some time ago), but I'm getting the transform to run from a build event

"%CommonProgramFiles(x86)%\microsoft shared\TextTemplating\$(VisualStudioVersion)\TextTransform.exe" -a !!build!true "$(ProjectDir)Properties\AssemblyInfo.tt"

but see Get Visual Studio to run a T4 Template on every build for some similar ideas.

I'm not sure this is the right solution, and in general I'd recommend against T4 due to the complexity it adds, but there you go.

edit: The partial class post linked above applies to properties, you should just be able to add attributes without creating a metadata class for the partial class.



Related Topics



Leave a reply



Submit