分类目录归档:.Net

.Net

.Net mvc自定义字符串截取(考虑全角/半角)

public static class HtmlHelpers
{
  public static string Truncate(this HtmlHelper helper, string inputString, int length)
  {
    string tempString = string.Empty;
    for (int i = 0, tempIndex = 0; i < inputString.Length; ++i, ++tempIndex)
    {
      if (System.Text.Encoding.UTF8.GetBytes(new char[] { inputString[i] }).Length > 1)
      {
        ++tempIndex;
      }
      if (tempIndex >= length)
      {
        tempString += "...";
        break;
      }
      tempString += inputString[i];
    }
    return tempString;
  }
}

C#中汉字排序简单示例(拼音/笔划)


class Program
    {
        static void Main(string[] args)
        {
            string[] arr = { "趙(ZHAO)", "錢(QIAN)", "孫(SUN)", "李(LI)", "周(ZHOU)", "吳(WU)", "鄭(ZHENG)", "王(WANG)"};
            //发音 LCID:0x00000804
            CultureInfo PronoCi = new CultureInfo(2052);
            //Thread.CurrentThread.CurrentCulture = new CultureInfo("en-us");
            Array.Sort(arr);
            Console.WriteLine("按发音排序:");
            for (int i = arr.GetLowerBound(0); i <= arr.GetUpperBound(0); i++)
                Console.WriteLine("[{0}]:t{1}", i, arr.GetValue(i));
            Console.WriteLine();
            //笔画数 LCID:0x00020804
            CultureInfo StrokCi = new CultureInfo(133124);
            Thread.CurrentThread.CurrentCulture = StrokCi;
            Array.Sort(arr);
            Console.WriteLine("按笔划数排序:");
            for (int i = arr.GetLowerBound(0); i <= arr.GetUpperBound(0); i++)
                Console.WriteLine("[{0}]:t{1}", i, arr.GetValue(i));
            Console.WriteLine();
            //zh-cn (拼音:简中)
            Thread.CurrentThread.CurrentCulture = new CultureInfo("zh-cn");
            Array.Sort(arr);
            Console.WriteLine("zh-cn:");
            for (int i = arr.GetLowerBound(0); i <= arr.GetUpperBound(0); i++)
                Console.WriteLine("[{0}]:t{1}", i, arr.GetValue(i));
            Console.WriteLine();
            //zh-tw (笔划数:繁中)
            Thread.CurrentThread.CurrentCulture = new CultureInfo("zh-tw");
            Array.Sort(arr);
            Console.WriteLine("zh-tw:");
            for (int i = arr.GetLowerBound(0); i <= arr.GetUpperBound(0); i++)
                Console.WriteLine("[{0}]:t{1}", i, arr.GetValue(i));
            Console.ReadKey();
        }
    }

如何让ASP.NET WEB API 默认返回JSON 格式

Web API 默认是返回JSON和XML的,在WebApiConfig的Register方法里可以去掉XML formatter

public static class WebApiConfig
{
  public static void Register(HttpConfiguration config)
  {
    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
    //remove xml, default json
    config.Formatters.Remove(config.Formatters.XmlFormatter);
  }
}

Web API:Handling Circular Object References

By default, the JSON and XML formatters write all objects as values. If two properties refer to the same object, or if the same object appears twice in a collection, the formatter will serialize the object twice. This is a particular problem if your object graph contains cycles, because the serializer will throw an exception when it detects a loop in the graph.
Consider the following object models and controller.

public class Employee
{
    public string Name { get; set; }
    public Department Department { get; set; }
}
public class Department
{
    public string Name { get; set; }
    public Employee Manager { get; set; }
}
public class DepartmentsController : ApiController
{
    public Department Get(int id)
    {
        Department sales = new Department() { Name = "Sales" };
        Employee alice = new Employee() { Name = "Alice", Department = sales };
        sales.Manager = alice;
        return sales;
    }
}

Invoking this action will cause the formatter to thrown an exception, which translates to a status code 500 (Internal Server Error) response to the client.
To preserve object references in JSON, add the following code to?Application_Start?method in the Global.asax file:

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling =
    Newtonsoft.Json.PreserveReferencesHandling.All;

Now the controller action will return JSON that looks like this:

{"$id":"1","Name":"Sales","Manager":{"$id":"2","Name":"Alice","Department":{"$ref":"1"}}}

Notice that the serializer adds an “$id” property to both objects. Also, it detects that the Employee.Department? property creates a loop, so it replaces the value with an object reference: {“$ref”:”1″}.
Object references are not standard in JSON. Before using this feature, consider whether your clients will be able to parse the results. It might be better simply to remove cycles from the graph. For example, the link from Employee back to Department is not really needed in this example.
To preserve object references in XML, you have two options. The simpler option is to add[DataContract(IsReference=true)]?to your model class. The?IsReference?parameter enables oibject references. Remember that?DataContract?makes serialization opt-in, so you will also need to add?DataMember?attributes to the properties:

[DataContract(IsReference=true)]
public class Department
{
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public Employee Manager { get; set; }
}

Now the formatter will produce XML similar to following:

<Department xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="i1"
            xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"
            xmlns="http://schemas.datacontract.org/2004/07/Models">
  <Manager>
    <Department z:Ref="i1" />
    <Name>Alice</Name>
  </Manager>
  <Name>Sales</Name>
</Department>

If you want to avoid attributes on your model class, there is another option: Create a new type-specificDataContractSerializer?instance and set?preserveObjectReferences?to?true?in the constructor. Then set this instance as a per-type serializer on the XML media-type formatter. The following code show how to do this:

var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
var dcs = new DataContractSerializer(typeof(Department), null, int.MaxValue,
    false, /* preserveObjectReferences: */ true, null);
xml.SetSerializer<Department>(dcs);

Routing in ASP.NET Web API

Routing Tables

In ASP.NET Web API, a?controller?is a class that handles HTTP requests. The public methods of the controller are called?action methods?or simply?actions. When the Web API framework receives a request, it routes the request to an action.
To determine which action to invoke, the framework uses a?routing table. The Visual Studio project template for Web API creates a default route:

routes.MapHttpRoute(
    name: "API Default",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

This route is defined in the WebApiConfig.cs file, which is placed in the App_Start directory:

For more information aboout the?WebApiConfig?class, see?Configuring ASP.NET Web API?.
If you self-host Web API, you must set the routing table directly on the?HttpSelfHostConfiguration?object. For more information, see?Self-Host a Web API.
Each entry in the routing table contains a?route template. The default route template for Web API is “api/{controller}/{id}”. In this template, “api” is a literal path segment, and {controller} and {id} are placeholder variables.
When the Web API framework receives an HTTP request, it tries to match the URI against one of the route templates in the routing table. If no route matches, the client receives a 404 error. For example, the following URIs match the default route:

  • /api/contacts
  • /api/contacts/1
  • /api/products/gizmo1

However, the following URI does not match, because it lacks the “api” segment:

  • /contacts/1

Note:?The reason for using “api” in the route is to avoid collisions with ASP.NET MVC routing. That way, you can have “/contacts” go to an MVC controller, and “/api/contacts” go to a Web API controller. Of course, if you don’t like this convention, you can change the default route table.
Once a matching route is found, Web API selects the controller and the action:

  • To find the controller, Web API adds “Controller” to the value of the?{controller}?variable.
  • To find the action, Web API looks at the HTTP method, and then looks for an action whose name begins with that HTTP method name. For example, with a GET request, Web API looks for an action that starts with “Get…”, such as “GetContact” or “GetAllContacts”.? This convention applies only to GET, POST, PUT, and DELETE methods. You can enable other HTTP methods by using attributes on your controller. We’ll see an example of that later.
  • Other placeholder variables in the route template, such as?{id},?are mapped to action parameters.

Let’s look at an example. Suppose that you define the following controller:

public class ProductsController : ApiController
{
    public void GetAllProducts() { }
    public IEnumerable<Product> GetProductById(int id) { }
    public HttpResponseMessage DeleteProduct(int id){ }
}

Here are some possible HTTP requests, along with the action that gets invoked for each:

HTTP Method URI Path Action Parameter
GET api/products GetAllProducts (none)
GET api/products/4 GetProductById 4
DELETE api/products/4 DeleteProduct 4
POST api/products (no match)

Notice that the?{id}?segment of the URI, if present, is mapped to the?id?parameter of the action. In this example, the controller defines two GET methods, one with an?id?parameter and one with no parameters.
Also, note that the POST request will fail, because the controller does not define a “Post…” method.

Routing Variations

The previous section described the basic routing mechanism for ASP.NET Web API. This section describes some variations.

HTTP Methods

Instead of using the naming convention for HTTP methods, you can explicitly specify the HTTP method for an action by decorating the action method with the?HttpGet,?HttpPut,?HttpPost, or?HttpDelete?attribute.
In the following example, the FindProduct method is mapped to GET requests:

public class ProductsController : ApiController
{
    [HttpGet]
    public Product FindProduct(id) {}
}

To allow multiple HTTP methods for an action, or to allow HTTP methods other than GET, PUT, POST, and DELETE, use the?AcceptVerbs?attribute, which takes a list of HTTP methods.

public class ProductsController : ApiController
{
    [AcceptVerbs("GET", "HEAD")]
    public Product FindProduct(id) { }
    // WebDAV method
    [AcceptVerbs("MKCOL")]
    public void MakeCollection() { }
}

Routing by Action Name

With the default routing template, Web API uses the HTTP method to select the action. However, you can also create a route where the action name is included in the URI:

routes.MapHttpRoute(
    name: "ActionApi",
    routeTemplate: "api/{controller}/{action}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

In this route template, the?{action}?parameter names the action method on the controller. With this style of routing, use attributes to specify the allowed HTTP methods. For example, suppose your controller has the following method:

public class ProductsController : ApiController
{
    [HttpGet]
    public string Details(int id);
}

In this case, a GET request for “api/products/details/1” would map to the Details method. This style of routing is similar to ASP.NET MVC, and may be appropriate for an RPC-style API.
You can override the action name by using the?ActionName?attribute. In the following example, there are two actions that map to “api/products/thumbnail/id. One supports GET and the other supports POST:

public class ProductsController : ApiController
{
    [HttpGet]
    [ActionName("Thumbnail")]
    public HttpResponseMessage GetThumbnailImage(int id);
    [HttpPost]
    [ActionName("Thumbnail")]
    public void AddThumbnailImage(int id);
}

Non-Actions

To prevent a method from getting invoked as an action, use the?NonAction?attribute. This signals to the framework that the method is not an action, even if it would otherwise match the routing rules.

// Not an action method.
[NonAction]
public string GetPrivateData() { ... }

Further Reading

This topic provided a high-level view of routing. For more detail, see?Routing and Action Selection, which describes exactly how the framework matches a URI to a route, selects a controller, and then selects the action to invoke.

Entity Framework Loading Related Entities

Entity Framework supports three ways to load related data – eager loading, lazy loading and explicit loading. The techniques shown in this topic apply equally to models created with Code First and the EF Designer.
 

Eagerly Loading

Eager loading is the process whereby a query for one type of entity also loads related entities as part of the query. Eager loading is achieved by use of the Include method. For example, the queries below will load blogs and all the posts related to each blog.

using (var context = new BloggingContext())
{
  // Load all blogs and related posts
  var blogs1 = context.Blogs
                .Include(b => b.Posts)
                .ToList();
  // Load one blogs and its related posts
  var blog1 = context.Blogs
                .Where(b => b.Name == “ADO.NET Blog”)
                .Include(b => b.Posts)
                .FirstOrDefault();
  // Load all blogs and related posts
  // using a string to specify the relationship
  var blogs2 = context.Blogs
                .Include(“Posts”)
                .ToList();
  // Load one blog and its related posts
  // using a string to specify the relationship
  var blog2 = context.Blogs
                .Where(b => b.Name == “ADO.NET Blog”)
                .Include(“Posts”)
                .FirstOrDefault();
}
Note that Include is an extension method in the System.Data.Entity namespace so make sure you are using that namespace.

Eagerly loading multiple levels

It is also possible to eagerly load multiple levels of related entities. The queries below show examples of how to do this for both collection and reference navigation properties.

using (var context = new BloggingContext())
{
  // Load all blogs, all related posts, and all related comments
  var blogs1 = context.Blogs
                 .Include(b => b.Posts.Select(p => p.Comments))
                 .ToList();
  // Load all users their related profiles, and related avatar
  var users1 = context.Users
                .Include(u => u.Profile.Avatar)
                .ToList();
  // Load all blogs, all related posts, and all related comments
  // using a string to specify the relationships
  var blogs2 = context.Blogs
                 .Include(“Posts.Comments”)
                 .ToList();
  // Load all users their related profiles, and related avatar
  // using a string to specify the relationships
  var users2 = context.Users
                .Include(“Profile.Avatar”)
                .ToList();
}
Note that it is not currently possible to filter which related entities are loaded. Include will always being in all related entities.
 

Lazy Loading

Lazy loading is the process whereby an entity or collection of entities is automatically loaded from the database the first time that a property referring to the entity/entities is accessed. When using POCO entity types, lazy loading is achieved by creating instances of derived proxy types and then overriding virtual properties to add the loading hook. For example, when using the Blog entity class defined below, the related Posts will be loaded the first time the Posts navigation property is accessed:

public class Blog
{
  public int BlogId { get; set; }
  public string Name { get; set; }
  public string Url { get; set; }
  public string Tags { get; set; }
  public virtual ICollection Posts { get; set; }
}

Turning off lazy loading for specific navigation properties

Lazy loading of the Posts collection can be turned off by making the Posts property non-virtual:

public class Blog
{
  public int BlogId { get; set; }
  public string Name { get; set; }
  public string Url { get; set; }
  public string Tags { get; set; }
  public ICollection Posts { get; set; }
}
Loading of the Posts collection can still be achieved using eager loading (see?Eagerly loading related entities?above) or the Load method (see?Explicitly loading related entities?below).

Turn off lazy loading for all entities

Lazy loading can be turned off for all entities in the context by setting a flag on the Configuration property. For example:

public class BloggingContext : DbContext
{
    public BloggingContext()
    {
        this.Configuration.LazyLoadingEnabled = false;
    }
}
Loading of related entities can still be achieved using eager loading (see?Eagerly loading related entities?above) or the Load method (see?Explicitly loading related entities?below).
 

Explicitly Loading

Even with lazy loading disabled it is still possible to lazily load related entities, but it must be done with an explicit call. To do so you use the Load method on the related entity’s entry. For example:

using (var context = new BloggingContext())
{
  var post = context.Posts.Find(2);
  // Load the blog related to a given post
  context.Entry(post).Reference(p => p.Blog).Load();
  // Load the blog related to a given post using a string
  context.Entry(post).Reference(“Blog”).Load();
  var blog = context.Blogs.Find(1);
  // Load the posts related to a given blog
  context.Entry(blog).Collection(p => p.Posts).Load();
  // Load the posts related to a given blog
  // using a string to specify the relationship
  context.Entry(blog).Collection(“Posts”).Load();
}
Note that the Reference method should be used when an entity has a navigation property to another single entity. On the other hand, the Collection method should be used when an entity has a navigation property to a collection of other entities.

Applying filters when explicitly loading related entities

The Query method provides access to the underlying query that the Entity Framework will use when loading related entities. You can then use LINQ to apply filters to the query before executing it with a call to a LINQ extension method such as ToList, Load, etc. The Query method can be used with both reference and collection navigation properties but is most useful for collections where it can be used to load only part of the collection. For example:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);
    // Load the posts with the ‘entity-framework’ tag related to a given blog
    context.Entry(blog)
        .Collection(b => b.Posts)
        .Query()
        .Where(p => p.Tags.Contains(“entity-framework”)
        .Load();
    // Load the posts with the ‘entity-framework’ tag related to a given blog
    // using a string to specify the relationship
    context.Entry(blog)
        .Collection(“Posts”)
        .Query()
        .Where(p => p.Tags.Contains(“entity-framework”)
        .Load();
}
When using the Query method it is usually best to turn off lazy loading for the navigation property. This is because otherwise the entire collection may get loaded automatically by the lazy loading mechanism either before or after the filtered query has been executed.
Note that while the relationship can be specified as a string instead of a lambda expression, the returned IQueryable is not generic when a string is used and so the Cast method is usually needed before anything useful can be done with it.
 

Using Query to count related entities without loading them

Sometimes it is useful to know how many entities are related to another entity in the database without actually incurring the cost of loading all those entities. The Query method with the LINQ Count method can be used to do this. For example:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);
    // Count how many posts the blog has
    var postCount = context.Entry(blog)
                          .Collection(b => b.Posts)
                          .Query()
                          .Count();
}